package com.pactera.asmp.server.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.pactera.asmp.server.common.constants.Constants;
import com.pactera.asmp.server.common.constants.ResultCode;
import com.pactera.asmp.server.common.exception.CustomException;
import com.pactera.asmp.server.dao.ConstantInfoMapper;
import com.pactera.asmp.server.dao.ProjectModuleMapper;
import com.pactera.asmp.server.dao.ScriptInfoMapper;
import com.pactera.asmp.server.entity.ProjectModuleDTO;
import com.pactera.asmp.server.entity.ProjectModuleSearch;
import com.pactera.asmp.server.entity.ScriptInfoDTO;
import com.pactera.asmp.server.entity.ScriptInfoSearchDTO;
import com.pactera.asmp.server.pojo.ScriptInfo;
import com.pactera.asmp.server.pojo.ScriptInfo2;
import com.pactera.asmp.server.pojo.User;
import com.pactera.asmp.server.utils.AssertUtil;
import com.pactera.asmp.server.utils.CacheBeanCopier;
import com.pactera.asmp.server.utils.PageDataResult;

import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ProjectName: asmp-server
 * @Package com.pactera.asmp.server.service
 * @ClassName: ScriptInfoServcieImpl
 * @Description: ScriptInfoServcieImpl
 * @Date 2020/07/24 16:34
 * @Author lvzhiqiang
 * @Version 1.0
 */
@Service
public class ScriptInfoServcieImpl implements IScriptInfoServcie {

    @Resource
    private ScriptInfoMapper mScriptInfoMapper;

    @Autowired
    private IScriptInfoHistoryService scriptInfoHistoryService;

    @Autowired
    private ConstantInfoMapper constantInfoMapper;

    @Autowired
    private IConstantInfoService constantInfoService;

    @Autowired
    private ProjectModuleMapper projectModuleMapper;

    @Autowired
    private UserService userService;

    @Override
    @Transactional
    public ScriptInfoDTO insert(ScriptInfoDTO scriptInfoDTO) {
        AssertUtil.notNull(scriptInfoDTO,"参数异常");

        User currUser = (User) SecurityUtils.getSubject().getPrincipal();
        if (currUser == null) throw new CustomException(ResultCode.PERMISSION_EXPIRE);
        scriptInfoDTO.setUserId(currUser.getId());


        ScriptInfo scriptInfoOld = new ScriptInfo();
        Integer status = Constants.SCRIPT_HISTORY_STATUS_1;
        Integer id = scriptInfoDTO.getId();
        if(!checkNameExist(id, scriptInfoDTO.getName(), scriptInfoDTO.getProjectModuleId())) {
            throw new CustomException(ResultCode.BUSINESS_SCRIPTNAME_EXISTED);
        }
        if (null != id && 0 != id) {
            // 脚本若是修改时，先取出老脚本
            status = Constants.SCRIPT_HISTORY_STATUS_2;
            scriptInfoOld = this.getScriptByPrimaryKey(id);
        } else {
            scriptInfoDTO.setTimeCreate(new Date());
            // 脚本若是新增，则先于脚本历史记录，目的是历史记录需获取脚本主键
            mScriptInfoMapper.insert(scriptInfoDTO);
            //关联常量
            if(CollectionUtils.isNotEmpty(scriptInfoDTO.getConstantIds())) {
                for(Long constantId:scriptInfoDTO.getConstantIds()) {
                    constantInfoMapper.insertScriptAndConstantRel((long)scriptInfoDTO.getId(), constantId);
                }
                constantInfoService.updateConstantServiceStatus(scriptInfoDTO.getConstantIds(), 1);
            }
        }

        // 变更脚本时也在历史记录表里放一份数据
        ScriptInfo scriptInfoNew = new ScriptInfo();
        CacheBeanCopier.copy(scriptInfoDTO, scriptInfoNew);
        scriptInfoHistoryService.handleParamAndInsert(scriptInfoOld, scriptInfoNew, status);

        // 脚本若是修改，则后于脚本历史记录，目的是获取修改前的脚本数据
        if (null != id && 0 != id) {
            mScriptInfoMapper.update(scriptInfoNew);
        }
        return scriptInfoDTO;
    }

    @Override
    public boolean delete(Integer id) {
        constantInfoMapper.deleteScriptAndConstantRel((long)id, null);
        return mScriptInfoMapper.deleteByPrimaryKey(id) >0 ;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(List<Integer> datas) {
        for (Integer id : datas) {
            ScriptInfo scriptInfoOld = this.getScriptByPrimaryKey(id);
            scriptInfoHistoryService.handleParamAndInsert(scriptInfoOld, new ScriptInfo(), Constants.SCRIPT_HISTORY_STATUS_3);
            constantInfoMapper.deleteScriptAndConstantRel((long)id, null);
        }
        return mScriptInfoMapper.deleteByPrimaryKeys(datas)>0;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean update(ScriptInfo scriptInfo) {

        User currUser = (User) SecurityUtils.getSubject().getPrincipal();
        if (currUser == null) throw new CustomException(ResultCode.PERMISSION_EXPIRE);
        if(!checkNameExist(scriptInfo.getId(), scriptInfo.getName(), scriptInfo.getProjectModuleId())) {
            throw new CustomException(ResultCode.BUSINESS_SCRIPTNAME_EXISTED);
        }
        scriptInfo.setUpdateUserId(currUser.getId());
        // 更新前先比较差异，并添加到历史记录表
        ScriptInfo scriptInfoOld = this.getScriptByPrimaryKey(scriptInfo.getId());
        scriptInfoHistoryService.handleParamAndInsert(scriptInfoOld, scriptInfo, Constants.SCRIPT_HISTORY_STATUS_2);

        List<Long> constantIds = constantInfoMapper.selectConstantIdsByScriptId(scriptInfo.getId());
        constantInfoMapper.deleteScriptAndConstantRel((long)scriptInfo.getId(), null);
        //关联常量
        if(CollectionUtils.isNotEmpty(scriptInfo.getConstantIds())) {
            for(Long constantId:scriptInfo.getConstantIds()) {
                constantInfoMapper.insertScriptAndConstantRel((long)scriptInfo.getId(), constantId);
            }
            constantInfoService.updateConstantServiceStatus(scriptInfo.getConstantIds(), 1);
            constantIds = constantIds.stream()
                    .filter(x -> !scriptInfo.getConstantIds().contains(x)).distinct().collect(Collectors.toList());
        }

        if(CollectionUtils.isNotEmpty(constantIds)) {
            constantInfoService.updateConstantServiceStatus(constantIds, 0);
        }
        return mScriptInfoMapper.update(scriptInfo) > 0;
    }

    @Override
    public PageDataResult select(ScriptInfoSearchDTO scriptInfoSearchDTO) {
        scriptInfoSearchDTO.setProjectIds(userService.getRelatedProject());
        PageDataResult pageDataResult = new PageDataResult();
        PageHelper.startPage(scriptInfoSearchDTO.getPage(), scriptInfoSearchDTO.getLimit());
        scriptInfoSearchDTO.setStatus(1);
        List<ScriptInfo> list = mScriptInfoMapper.select(scriptInfoSearchDTO);
        PageInfo<ScriptInfo> pageInfo = new PageInfo<>(list);
        pageDataResult.setTotals(Long.valueOf(pageInfo.getTotal()).intValue());
        pageDataResult.setList(list);
        return pageDataResult;
    }

    @Override
    public List<ScriptInfo> selectScriptByGroupIds(List<Integer> groupIds) {
        return mScriptInfoMapper.selectScriptByGroupIds(groupIds);
    }

    @Override
    public List<ScriptInfo2> selectScriptByScriptIds(List<Integer> scriptIds) {
        return mScriptInfoMapper.selectScriptByScriptIds(scriptIds);
    }

    @Override
    public List<ScriptInfo2> selectScriptByScriptIdsWithoutStatus(List<Integer> scriptIds) {
        return mScriptInfoMapper.selectScriptByScriptIdsWithoutStatus(scriptIds);
    }

    @Override
    public ScriptInfo selectScriptByScriptId(Integer scriptId) {
        return mScriptInfoMapper.selectScriptByScriptId(scriptId);
    }

    @Override
    public Map<Integer,Integer> getScriptModuleByIds(Integer[] ids) {
        if(ids == null || ids.length == 0) return null;
        List<ScriptInfo> scriptModuleByIds = mScriptInfoMapper.getScriptModuleByIds(ids);
        Map<Integer,Integer> result = new HashMap<>();

        for (ScriptInfo scriptInfo : scriptModuleByIds){
            result.put(scriptInfo.getId(),scriptInfo.getProjectModuleId());
        }
        return result;
    }

    private ScriptInfo getScriptByPrimaryKey(Integer id) {
        ScriptInfoSearchDTO scriptInfoSearchDTO = new ScriptInfoSearchDTO();
        scriptInfoSearchDTO.setId(id);
        scriptInfoSearchDTO.setStatus(1);
        List<ScriptInfo> scriptInfos = mScriptInfoMapper.select(scriptInfoSearchDTO);
        if (scriptInfos.isEmpty()) {
            throw new CustomException(ResultCode.BUSINESS_SCRIPT_NOT_EXISTED);
        }
        return scriptInfos.get(0);
    }

    @Override
    public List<Long> selectRelConstantList(Integer scriptId) {
        if (null == scriptId || 0 == scriptId) {
            throw new CustomException(ResultCode.PARAM_IS_BLANK);
        }
        return constantInfoMapper.selectConstantIdsByScriptId(scriptId);
    }

    private boolean checkNameExist(Integer id, String name, Integer projectModuleId) {
        List<ProjectModuleDTO> projectModuleDTOs = projectModuleMapper.select(new ProjectModuleSearch() {{setId(projectModuleId);}});
        if(CollectionUtils.isEmpty(projectModuleDTOs)) {
            return true;
        }
        // 获取模块信息
        List<ProjectModuleDTO> proModules = projectModuleMapper.select(new ProjectModuleSearch() {{setProjectId(projectModuleDTOs.get(0).getProjectId());}});
        if(CollectionUtils.isEmpty(proModules)) {
            return true;
        }
        List<ScriptInfo2> scriptInfos = mScriptInfoMapper.getScriptByNamesAndProjecModuletIds(Lists.newArrayList(name), proModules.stream().map(ProjectModuleDTO::getId).collect(Collectors.toList()));
        if(CollectionUtils.isEmpty(scriptInfos)) {
            return true;
        }
        if(null == id) {
            return false;
        }
        return scriptInfos.stream().anyMatch(scriptInfo -> {
            return id.intValue() == scriptInfo.getId().intValue();
        });
    }
}
